#---------------------------------------------------------
# Are Intermediary Constraints Priced?
# Du, Hébert, Huber
# The Review of Financial Studies 2022
#---------------------------------------------------------

#---------------------------------------------------------
# Calculate spot basis, forward basis, and forward arbitrage returns
# (0) Merge in settlement data
# (1) Spot basis for different tenor and type of rate
# (2) Forward basis for different forward and spot tenor
# (3) Single currency forward-arbitrage trade returns (lining up future spot basis with expiry)
# NOTE: ibor forward basis are taken directly from FRA
#       due to availability, OIS basis are over 6 currencies, IBOR basis are over 6 + CHF
#---------------------------------------------------------

#---------------------------------------------------------
# Set-up
#---------------------------------------------------------

if (exists('script_path')) {
  rm(list = setdiff(ls(), 'script_path'))
} else {
  args = commandArgs(trailingOnly = T)
  if (length(args) > 0) {
    script_path <- args[1]  
    rm(list = setdiff(ls(), 'script_path'))
  } else {
    rm(list = ls())
    script_path <- '~/Dropbox/ForwardArbitrage/CodeReplication/'
  } 
}

# Inputs
source(paste0(script_path, 'RCode/ArbFunc.R'))
load(paste0(script_path, 'OutputInterim/All_Rates.RData')) 
load(paste0(script_path, 'OutputInterim/Settlement_AllDays.RData'))

# Initialization
currency_names6 <- generate_currency_names(number_of_currencies = 6) #AUD, CAD, GBP, EUR, JPY, CHF
domestic_day_count <- currency_convention$day_count[rownames(currency_convention) == 'USD']
all_rates <- all_rates[, c('Date', names(all_rates)[substr(names(all_rates), 1, 3) %in% c(currency_names6, 'USD')]), with = F]

# Forward basis to calculate
fwd_table <- data.frame(
  fwd_start_mth = c(1, 2, 3, 1, 4, 1, 3, 6, 9, 12, 24, 36, 48, 60),
  contract_mth = c(1, 1, 1, 2, 2, 3, 3, 3, 3, 12, 12, 12, 12, 60))
fwd_table$fwd_end_mth <- fwd_table$fwd_start_mth + fwd_table$contract_mth

for (r in 1:nrow(fwd_table)) {
  fwd_table$fwd_start_tenor[r] <- tenor_lookup$tenor_short_name[match(fwd_table$fwd_start_mth[r], tenor_lookup$tenor_month)]
  fwd_table$contract_tenor[r] <- tenor_lookup$tenor_short_name[match(fwd_table$contract_mth[r], tenor_lookup$tenor_month)]
  fwd_table$fwd_end_tenor[r] <- tenor_lookup$tenor_short_name[match(fwd_table$fwd_end_mth[r], tenor_lookup$tenor_month)]
}

# Returns to calculate (not calculating all permutations for IBOR and IBOR-OIS)
return_table_master <- fwd_table[(fwd_table$fwd_start_mth == 1 & (fwd_table$contract_mth == 1 | fwd_table$contract_mth == 3)) |
                                   (fwd_table$fwd_start_mth == 3 & fwd_table$contract_mth == 3), ]

#---------------------------------------------------------
# Merge in settlement dates
#---------------------------------------------------------

list_merge <- list()
list_merge[[1]] <- all_rates[Date >= start_date]
settlement_tenor <- c('spot', 'one_mth', 'two_mth', 'three_mth', 'four_mth', 'five_mth', 'six_mth', 
                      'seven_mth', 'eight_mth', 'nine_mth', 'ten_mth', 'eleven_mth', 'one_yr')
list_settlement <- list_settlement[names(list_settlement) %in% currency_names6]

## Exclude dates that either start on a US bond market holiday or settle on a US bond market holiday
temp <- data.table(Date = seq.Date(from = ymd('2003-01-01'), to = ymd('2021-05-31'), by = 1))
temp[, ffr := all_rates$USD_ois_index[match(temp$Date, all_rates$Date)]]
holiday_list <- temp[is.na(ffr), Date] #includes Saturdays and Sundays

for (l in 1:length(list_settlement)) {
  unwanted_names <- names(list_settlement[[l]])[!(names(list_settlement[[l]]) %in% c('Date', settlement_tenor))]
  temp <- list_settlement[[l]][, (unwanted_names) := NULL]
  temp <- temp[!(Date %in% holiday_list)]
  for (j in 1:ncol(temp)) {
    set(temp, which(temp[[j]] %in% holiday_list), j, 0)
  }
  list_merge[[l + 1]] <- temp
}

rates_and_dates <- multiple_reduced_join(merge_key = 'Date', merge_all_x = TRUE, merge_all_y = FALSE,
                                         data_list = list_merge)  
names(rates_and_dates)[(ncol(all_rates) + 1) : ncol(rates_and_dates)] <- 
  apply(expand.grid(x = tenor_lookup$tenor_short_name[match(settlement_tenor, tenor_lookup$tenor_name)], y = names(list_settlement)), 1, 
        function(duple) paste(duple[2], 'settlement', duple[1], sep = '_'))

## Extend settlement for spot, 1M, 3M, 4M, 6M
for (tenor in c('spot', '1M', '3M', '4M', '6M')) {
  temp <- data.table(read_excel(paste0(script_path, 'DataSkeleton/SettlementDates/daycounts_08032021.xlsx'), sheet = tenor))
  temp[, Date := ymd(date)]
  temp <- cbind(temp$Date, temp[, lapply(.SD, function(x) mdy(x)), .SDcols = names(temp)[grepl('Curncy', names(temp))]])
  names(temp) <- c('Date', paste(c('AUD', 'CAD', 'CHF', 'DKK', 'EUR', 'GBP', 'JPY', 'NOK', 'NZD', 'SEK'), 'settlement', tenor, sep = '_'))
  temp <- temp[!(Date %in% holiday_list)]
  for (c in 1:length(currency_names6)) {
    rates_and_dates[Date > ymd('2020-12-31'), paste(currency_names6[c], 'settlement', tenor, sep = '_') := 
                      temp[, paste(currency_names6[c], 'settlement', tenor, sep = '_'), with = F][match(rates_and_dates[Date > ymd('2020-12-31'), Date], temp$Date)]]
  }
}

## Create expiry look up from spot settlement, including new data in 2021
expiry_lookup <- rates_and_dates[, c('Date', names(rates_and_dates)[grepl('settlement_spot', names(rates_and_dates))]), with = F]
expiry_lookup <- expiry_lookup[complete.cases(expiry_lookup)]

#---------------------------------------------------------
# Calculate spot basis for OIS and IBOR
#---------------------------------------------------------

# Spot basis for all possible pairs of OIS, LIBOR < 1Y
# Tenor included: 1M, 2M, 3M, 4M, 6M, 9M
# NOTE: calculate everything against USD and then form cross-currency pairs

for (rate in c('ois', 'ibor')) {
  pair_extended <- single_table
  
  for (i in 1:nrow(pair_extended)) {
    fund_currency <- pair_extended$fund_currency[i]
    invest_currency <- pair_extended$invest_currency[i]
    
    print(paste(fund_currency, '-', invest_currency, 'spot', rate, '-', rate, 'basis'))
    fund_day_count <- ifelse(fund_currency == 'USD', domestic_day_count, currency_convention$day_count[rownames(currency_convention) == paste(fund_currency, rate, sep = '_')])
    invest_day_count <- ifelse(invest_currency == 'USD', domestic_day_count, currency_convention$day_count[rownames(currency_convention) == paste(invest_currency, rate, sep = '_')])
    
    for (spot_tenor in c(paste0(c(seq.int(from = 1, to = 4, by = 1), 6, 9), 'M'))) {
      rates_and_dates[, fund_contract_dur := as.numeric(get(paste(ifelse(fund_currency == 'USD', invest_currency, fund_currency), 'settlement', spot_tenor, sep = '_')) - 
                                                          get(paste(ifelse(fund_currency == 'USD', invest_currency, fund_currency), 'settlement_spot', sep = '_')))]
      rates_and_dates[, invest_contract_dur := as.numeric(get(paste(ifelse(invest_currency == 'USD', fund_currency, invest_currency), 'settlement', spot_tenor, sep = '_')) - 
                                                            get(paste(ifelse(invest_currency == 'USD', fund_currency, invest_currency), 'settlement_spot', sep = '_')))]
      
      if (fund_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', spot_tenor, sep = '_') := 
                          compute_log_fund_invest_basis(fund_annualized_rate = get(paste(fund_currency, rate, spot_tenor, sep = '_')),
                                                        invest_annualized_rate = get(paste(invest_currency, rate, spot_tenor, sep = '_')),
                                                        fund_start_exchange_rate = 1,
                                                        fund_end_exchange_rate = 1, 
                                                        invest_start_exchange_rate = get(paste(invest_currency, 'fx_spot', sep = '_')),
                                                        invest_end_exchange_rate = get(paste(invest_currency, 'fx', spot_tenor, sep = '_')), 
                                                        USD_day_count = domestic_day_count,
                                                        fund_day_count = fund_day_count,
                                                        invest_day_count = invest_day_count,
                                                        fund_duration = fund_contract_dur,
                                                        invest_duration = invest_contract_dur)]
      } else if (invest_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', spot_tenor, sep = '_') := 
                          compute_log_fund_invest_basis(fund_annualized_rate = get(paste(fund_currency, rate, spot_tenor, sep = '_')),
                                                        invest_annualized_rate = get(paste(invest_currency, rate, spot_tenor, sep = '_')),
                                                        fund_start_exchange_rate = get(paste(fund_currency, 'fx_spot', sep = '_')),
                                                        fund_end_exchange_rate = get(paste(fund_currency, 'fx', spot_tenor, sep = '_')), 
                                                        invest_start_exchange_rate = 1,
                                                        invest_end_exchange_rate = 1, 
                                                        USD_day_count = domestic_day_count,
                                                        fund_day_count = fund_day_count,
                                                        invest_day_count = invest_day_count,
                                                        fund_duration = fund_contract_dur,
                                                        invest_duration = invest_contract_dur)]
      } else {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', spot_tenor, sep = '_') := 
                          compute_log_fund_invest_basis(fund_annualized_rate = get(paste(fund_currency, rate, spot_tenor, sep = '_')),
                                                        invest_annualized_rate = get(paste(invest_currency, rate, spot_tenor, sep = '_')),
                                                        fund_start_exchange_rate = get(paste(fund_currency, 'fx_spot', sep = '_')),
                                                        fund_end_exchange_rate = get(paste(fund_currency, 'fx', spot_tenor, sep = '_')), 
                                                        invest_start_exchange_rate = get(paste(invest_currency, 'fx_spot', sep = '_')),
                                                        invest_end_exchange_rate = get(paste(invest_currency, 'fx', spot_tenor, sep = '_')), 
                                                        USD_day_count = domestic_day_count,
                                                        fund_day_count = fund_day_count,
                                                        invest_day_count = invest_day_count,
                                                        fund_duration = fund_contract_dur,
                                                        invest_duration = invest_contract_dur)]
      }
    }
  }
}

for (rate in c('ois', 'ibor')) {
  print(paste('All other spot', rate, '-', rate, 'basis'))
  if (rate == 'ois') {
    pair_extended <- ois_table #ois_extended contains all pairs, ois_table just have the top 10.
  } else if (rate == 'ibor') {
    pair_extended <- ibor_table #ibor_extended
  }
  
  for (i in 1:nrow(pair_extended)) {
    fund_currency <- pair_extended$fund_currency[i]
    invest_currency <- pair_extended$invest_currency[i]
    
    for (spot_tenor in c(paste0(c(seq.int(from = 1, to = 4, by = 1), 6, 9), 'M'))) {
      if (fund_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', spot_tenor, sep = '_') := 
                          0 - get(paste(invest_currency, 'USD', rate, 'spot_log_basis', spot_tenor, sep = '_'))]
      } else if (invest_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', spot_tenor, sep = '_') := 
                          get(paste(fund_currency, 'USD', rate, 'spot_log_basis', spot_tenor, sep = '_'))]
      } else {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', spot_tenor, sep = '_') := 
                          get(paste(fund_currency, 'USD', rate, 'spot_log_basis', spot_tenor, sep = '_')) - 
                          get(paste(invest_currency, 'USD', rate, 'spot_log_basis', spot_tenor, sep = '_'))]
      }
    }
  }
}

#---------------------------------------------------------
# Calculate forward basis for OIS < 1Y and IBOR 1M-f-3M, 3M-f-3M from first principle
# NOTE: (1) USD_FX ois rate is the forward ois rate defined for each corresponding currency pair to accommodate duration differences; 
#           not necessary for IBOR because use quoted fwd rates
#       (2) FX-USD bases are used in portfolio formations and in backing out all cross-currency pairs
#---------------------------------------------------------

for (rate in c('ois')) {
  currency_names <- currency_names6
  
  for (c in 1:length(currency_names)) {
    foreign_day_count <- currency_convention$day_count[rownames(currency_convention) == paste(currency_names[c], rate, sep = '_')]
    print(paste(currency_names[c], 'forward', rate, 'basis'))
    
    for (f in 1:sum(fwd_table$fwd_end_mth <= 12)) {
      contract_tenor <- fwd_table[f, 'contract_tenor']
      fwd_start_tenor <- fwd_table[f, 'fwd_start_tenor']
      fwd_end_tenor <- fwd_table[f, 'fwd_end_tenor']
      
      rates_and_dates[, implied_contract_dur := as.numeric(get(paste(currency_names[c], 'settlement', fwd_end_tenor, sep = '_')) - get(paste(currency_names[c], 'settlement', fwd_start_tenor, sep = '_')))]
      rates_and_dates[, fwd_start_dur := as.numeric(get(paste(currency_names[c], 'settlement', fwd_start_tenor, sep = '_')) - get(paste(currency_names[c], 'settlement_spot', sep = '_')))]
      rates_and_dates[, fwd_end_dur := as.numeric(get(paste(currency_names[c], 'settlement', fwd_end_tenor, sep = '_')) - get(paste(currency_names[c], 'settlement_spot', sep = '_')))]
      
      # need to first manually calculate what the forward rates are; note that the forward USD ois rate is different for each currency due to settlements
      rates_and_dates[, paste('USD', currency_names[c], rate, fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := compute_annulized_rate(
        compounded_interest = (compute_compound_interest(rate_annualized = get(paste('USD', rate, fwd_end_tenor, sep = '_')),
                                                         day_count = domestic_day_count, duration = fwd_end_dur) /
                                 compute_compound_interest(rate_annualized = get(paste('USD', rate, fwd_start_tenor, sep = '_')),
                                                           day_count = domestic_day_count, duration = fwd_start_dur)),
        day_count = domestic_day_count, duration = implied_contract_dur)]
      rates_and_dates[, paste(currency_names[c], rate, fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := compute_annulized_rate(
        compounded_interest = (compute_compound_interest(rate_annualized = get(paste(currency_names[c], rate, fwd_end_tenor, sep = '_')),
                                                         day_count = foreign_day_count, duration = fwd_end_dur) /
                                 compute_compound_interest(rate_annualized = get(paste(currency_names[c], rate, fwd_start_tenor, sep = '_')),
                                                           day_count = foreign_day_count, duration = fwd_start_dur)),
        day_count = foreign_day_count, duration = implied_contract_dur)]
      
      rates_and_dates[, paste(currency_names[c], 'USD', rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                        compute_log_currency_basis(domestic_annualized_rate = get(paste('USD', currency_names[c], rate, fwd_start_tenor, 'fwd', contract_tenor, sep = '_')), 
                                                   foreign_annualized_rate = get(paste(currency_names[c], rate, fwd_start_tenor, 'fwd', contract_tenor, sep = '_')),
                                                   start_exchange_rate = get(paste(currency_names[c], 'fx', fwd_start_tenor, sep = '_')),
                                                   end_exchange_rate = get(paste(currency_names[c], 'fx', fwd_end_tenor, sep = '_')),
                                                   domestic_day_count = domestic_day_count,
                                                   foreign_day_count = foreign_day_count,
                                                   duration = implied_contract_dur)]
    }
  }
}

for (rate in c('ibor')) {
  currency_names <- currency_names6
  return_table <- return_table_master[2:3, ]
  
  for (c in 1:length(currency_names)) {
    print(paste(currency_names[c], 'forward', rate, 'basis'))
    foreign_day_count <- currency_convention$day_count[rownames(currency_convention) == paste(currency_names[c], rate, sep = '_')]
    
    for (j in 1:nrow(return_table)) {
      contract_tenor <- return_table[j, 'contract_tenor']
      fwd_start_tenor <- return_table[j, 'fwd_start_tenor']
      fwd_end_tenor <- return_table[j, 'fwd_end_tenor']
      
      rates_and_dates[, implied_contract_dur := as.numeric(get(paste(currency_names[c], 'settlement', fwd_end_tenor, sep = '_')) - get(paste(currency_names[c], 'settlement', fwd_start_tenor, sep = '_')))]
      rates_and_dates[, paste(currency_names[c], 'USD', rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                        compute_log_currency_basis(domestic_annualized_rate = get(paste('USD', rate, fwd_start_tenor, 'fwd', contract_tenor, sep = '_')), 
                                                   foreign_annualized_rate = get(paste(currency_names[c], rate, fwd_start_tenor, 'fwd', contract_tenor, sep = '_')),
                                                   start_exchange_rate = get(paste(currency_names[c], 'fx', fwd_start_tenor, sep = '_')),
                                                   end_exchange_rate = get(paste(currency_names[c], 'fx', fwd_end_tenor, sep = '_')),
                                                   domestic_day_count = domestic_day_count,
                                                   foreign_day_count = foreign_day_count,
                                                   duration = implied_contract_dur)]
    }
  }
}

# All cross-currency pairs
for (rate in c('ois', 'ibor')) {
  print(paste('All other forward', rate, '-', rate, 'basis'))
  if (rate == 'ois') {
    pair_table <- rbind(ois_table)
    return_table <- return_table_master
  } else if (rate == 'ibor') {
    pair_table <- rbind(ibor_table)
    return_table <- return_table_master[2:3, ]
  }
  
  for (i in 1:nrow(pair_table)) {
    fund_currency <- pair_table$fund_currency[i]
    invest_currency <- pair_table$invest_currency[i]
    
    for (j in 1:nrow(return_table)) {
      contract_tenor <- return_table[j, 'contract_tenor']
      fwd_start_tenor <- return_table[j, 'fwd_start_tenor']
      fwd_end_tenor <- return_table[j, 'fwd_end_tenor']
      
      if (fund_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                          0 - get(paste(invest_currency, 'USD', rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_'))]
      } else if (invest_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                          get(paste(fund_currency, 'USD', rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_'))]
      } else {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                          get(paste(fund_currency, 'USD', rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_')) - 
                          get(paste(invest_currency, 'USD', rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_'))]
      }        
    }
  }
}

#---------------------------------------------------------
# Remove outliers: |basis| > 1000 bps
#---------------------------------------------------------

basis_names <- names(rates_and_dates)[grepl('basis', names(rates_and_dates))]
temp <- copy(rates_and_dates)
rates_and_dates <- rates_and_dates[, (basis_names) := lapply(.SD, function(x) replace(x, which(abs(x) > 10), NA)), .SDcols = basis_names]

#---------------------------------------------------------
# Calculate return to forward arbitrage trades in individual currencies for select tenors
# OIS and IBOR, 1M-fwd 1M (OIS only), 1M-fwd 3M, 3M-fwd 3M
# NOTE: need to bring back future spot basis based on EXPIRY and not settlement dates;
#       for this reason, again, calculate relative to USD first and then calculate cross-currency pairs
#---------------------------------------------------------

# Single currencies
for (rate in c('ois', 'ibor')) {
  if (rate == 'ois') {
    pair_table <- rbind(single_table)
    return_table <- return_table_master
  } else if (rate == 'ibor') {
    pair_table <- rbind(single_table)
    return_table <- return_table_master[2:3, ]
  }
  
  for (i in 1:nrow(pair_table)) {
    fund_currency <- pair_table$fund_currency[i]
    invest_currency <- pair_table$invest_currency[i]
    settlement_currency <- ifelse(fund_currency == 'USD', invest_currency, fund_currency)
    print(paste(fund_currency, '-', invest_currency, rate, '-', rate, 'forward arbitrage return'))
    
    for (j in 1:nrow(return_table)) {
      contract_tenor <- return_table[j, 'contract_tenor']
      fwd_start_tenor <- return_table[j, 'fwd_start_tenor']
      fwd_end_tenor <- return_table[j, 'fwd_end_tenor']
      
      # Find expiry using fund calendar as this is the first chance to unwind the forward basis
      # (1) check only valid settlement dates are included, (2) use first available if there are duplicates due to holidays
      expiry_temp <- expiry_lookup[, c('Date', paste(settlement_currency, 'settlement_spot', sep = '_')), with = FALSE]
      expiry_temp <- expiry_temp[!duplicated(expiry_temp[, paste(settlement_currency, 'settlement_spot', sep = '_'), with = FALSE])]
      
      rates_and_dates[, paste(settlement_currency, 'expiry', fwd_start_tenor, sep = '_') := 
                        expiry_temp$Date[match(unlist(rates_and_dates[, paste(settlement_currency, 'settlement', fwd_start_tenor, sep = '_'), with = FALSE]), 
                                               unlist(expiry_temp[, paste(settlement_currency, 'settlement_spot', sep = '_'), with = FALSE]))]]        
      
      # Align future spot to calculate return
      rates_and_dates[, paste(fund_currency, invest_currency, rate, 'future_log_basis', contract_tenor, 'in', fwd_start_tenor, sep = '_') := 
                        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'spot_log_basis', contract_tenor, sep = '_'), with = FALSE]
                      [match(unlist(rates_and_dates[, paste(settlement_currency, 'expiry', fwd_start_tenor, sep = '_'), with = FALSE]), unlist(rates_and_dates[, Date]))]]        
      
      rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') :=
                        get(paste(fund_currency, invest_currency, rate, 'log_basis', fwd_start_tenor, 'fwd', contract_tenor, sep = '_')) -
                        get(paste(fund_currency, invest_currency, rate, 'future_log_basis', contract_tenor, 'in', fwd_start_tenor, sep = '_'))]
      
      rates_and_dates[is.na(get(paste(fund_currency, invest_currency, rate, 'spot_log_basis', contract_tenor, sep = '_'))),
                      paste(fund_currency, invest_currency, rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := NA]
    }
  }
}

# Cross-currency pairs
for (rate in c('ois', 'ibor')) {
  print(paste('All other', rate, 'returns'))
  if (rate == 'ois') {
    pair_table <- rbind(ois_table)
    return_table <- return_table_master
  } else if (rate == 'ibor') {
    pair_table <- rbind(ibor_table)
    return_table <- return_table_master[2:3, ]
  }
  
  for (i in 1:nrow(pair_table)) {
    fund_currency <- pair_table$fund_currency[i]
    invest_currency <- pair_table$invest_currency[i]
    
    for (j in 1:nrow(return_table)) {
      contract_tenor <- return_table[j, 'contract_tenor']
      fwd_start_tenor <- return_table[j, 'fwd_start_tenor']
      fwd_end_tenor <- return_table[j, 'fwd_end_tenor']
      
      if (fund_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                          0 - get(paste(invest_currency, 'USD', rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_'))]
      } else if (invest_currency == 'USD') {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                          get(paste(fund_currency, 'USD', rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_'))]
      } else {
        rates_and_dates[, paste(fund_currency, invest_currency, rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_') := 
                          get(paste(fund_currency, 'USD', rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_')) - 
                          get(paste(invest_currency, 'USD', rate, 'log_return', fwd_start_tenor, 'fwd', contract_tenor, sep = '_'))]
      }        
    }
  }
}

#---------------------------------------------------------
# Organize
#---------------------------------------------------------
# Aggregate all spot, forward, future bases and forward arbitrage returns in a data table, keep only rows where there is at least one observation
basis_and_returns <- rates_and_dates[Date >= start_date & Date <= trading_end_date, 
                                     c('Date', names(rates_and_dates)[grepl('spot_log_basis', names(rates_and_dates))],
                                       names(rates_and_dates)[grepl('log_basis', names(rates_and_dates)) & grepl('fwd', names(rates_and_dates))],
                                       names(rates_and_dates)[grepl('future_log_basis', names(rates_and_dates)) & grepl('in', names(rates_and_dates))],
                                       names(rates_and_dates)[grepl('log_return', names(rates_and_dates)) & grepl('fwd', names(rates_and_dates))]), 
                                     with = FALSE] 
basis_and_returns <- basis_and_returns[apply(basis_and_returns[, -1], 1, function(x) sum(!is.na(x)) > 0), ]

# Create sample period and save
basis_and_returns <- create_sample_period(number_of_periods = number_of_periods, data_table = basis_and_returns,
                                          date_variable = 'Date', period_order = period_order)

for (settlement_currency in c('AUD', 'JPY')) {
  for (tenor in c('1M', '3M', '4M')) {
    basis_and_returns[, paste(settlement_currency, 'settlement', tenor, sep = '_') := 
                        rates_and_dates[, get(paste(settlement_currency, 'settlement', tenor, sep = '_'))][match(basis_and_returns$Date, rates_and_dates$Date)]]        
  }
}

# Save
save(basis_and_returns, file = paste0(script_path, 'OutputInterim/Basis_and_Returns_1st.RData'))
save(rates_and_dates, file = paste0(script_path, 'OutputInterim/All_Rates_Dates_Basis_Returns.RData'))
names(rates_and_dates)[grepl('future_log_basis', names(rates_and_dates)) & !(grepl('portfolio', names(rates_and_dates)))] <-
  sub('future_log_basis', 'fut_log_b', names(rates_and_dates)[grepl('future_log_basis', names(rates_and_dates)) & !(grepl('portfolio', names(rates_and_dates)))])
names(rates_and_dates)[grepl('ibor_log_return', names(rates_and_dates)) & !(grepl('portfolio', names(rates_and_dates)))] <-
  sub('return', 'ret', names(rates_and_dates)[grepl('ibor_log_return', names(rates_and_dates)) & !(grepl('portfolio', names(rates_and_dates)))])
write_dta(rates_and_dates, paste0(script_path, 'OutputInterim/All_Rates_Dates_Basis_Returns.dta'))
